// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: hyuan

#pragma once

#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <map>

#include "coin2/exchange/base/order/rate_tracker.h"

namespace coin2::exchange::okex_common::api_util {

enum QueryType : int {
  UNKNOWN = 0,
  BALANCE = 1,
  POSITION = 2,
  OPEN_ORDER = 3,
  HIST_ORDER = 4,
  FILL_ORDER = 5,
  DEPOSIT = 6,
  WITHDRAWAL = 7
};

class OkexRateTracker : public coin2::exchange::base::order::IRateTracker {
 public:
  using RateTracker = coin2::exchange::base::order::RateTracker;
  using ActionType = coin2::exchange::base::order::IRateTracker::ActionType;
  using RateLimitReportWriter = coin2::exchange::base::order::RateLimitReportWriter;
  using FixedWindowRateTracker = coin2::exchange::base::order::FixedWindowRateTracker;
  using RateLimitInfoSpotOkex = coin2::exchange::order::RateLimitInfoSpotOkex;
  using OrderRateTrackerMap = std::map<std::string, std::unique_ptr<FixedWindowRateTracker>>;

 public:
  explicit OkexRateTracker(
      const MarketExchangeApi& mea,
      const coin2::exchange::order::OrderConnectionConfig& cfg);

  void SetRateLimitInfo(int32_t rate_limit, int32_t rate_limit_remaining) override { CHECK(false); }
  void Push(int32_t num_reqs, int64_t timestamp, ActionType action_type) override {}
  bool HasEnoughRateLimit(int64_t timestamp, ActionType action_type) override { return true; };
  void ParseRateLimitInfo(const std::shared_ptr<AsioHttpContext>&, ActionType) override {}
  void SetRateLimitInfoString(const google::protobuf::Message* message) override;
  void WriteRateLimitReportMessage(OrderLoggerCB callback) override;
  double GetMaxRateUsedRatio(int64_t timestamp, ActionType action_type) override;

  bool HasEnoughRateLimit(int64_t timestamp, ActionType action_type, QueryType query_type,
      const std::string& instrument);
  void Push(int32_t num_reqs, int64_t timestamp, ActionType action_type, QueryType query_type,
      const std::string& instrument);

 private:
  void InitRateTrackerWithSecondTracker(
      const RateLimitInfoSpotOkex* rate_config,
      std::string_view mea_str,
      std::string_view example);

 private:
  FixedWindowRateTracker* GetOrderRateTracker(
      OrderRateTrackerMap* ord_rate_tracker_map,
      const std::string& map_name,
      const std::string& instrument);
  FixedWindowRateTracker* GetPlaceOrderRateTracker(const std::string& instrument);
  FixedWindowRateTracker* GetCancelOrderRateTracker(const std::string& instrument);

  double GetMaxRateUsedRatio(
      const OrderRateTrackerMap& order_rate_tracker_map,
      int64_t timestamp) {
    double max_order_ratio = 0.;
    for (auto itr = order_rate_tracker_map.begin(); itr != order_rate_tracker_map.end(); itr++) {
      const std::unique_ptr<FixedWindowRateTracker>& rate_tracker = itr->second;
      if (rate_tracker) {
        auto max_reqs = rate_tracker->MaxReq();
        auto remaining = rate_tracker->GetRemaining(timestamp);
        assert(max_reqs > 0);
        auto order_ratio = 1.0 * (max_reqs - remaining) / max_reqs;
        if (order_ratio - max_order_ratio > 1e-6) {
          max_order_ratio = order_ratio;
        }
      }
    }
    return max_order_ratio;
  }

  double GetMaxRateUsedRatio(
      const std::unique_ptr<FixedWindowRateTracker>& rate_tracker,
      int64_t timestamp) {
    double rate_used_ratios = 0.0;
    if (rate_tracker) {
      auto max_reqs = rate_tracker->MaxReq();
      auto remaining = rate_tracker->GetRemaining(timestamp);
      assert(max_reqs > 0);
      rate_used_ratios = 1.0 * (max_reqs - remaining) / max_reqs;
    }
    return rate_used_ratios;
  }

 private:
  int32_t ord_reqs_;
  int64_t ord_nsecs_;
  OrderRateTrackerMap ord_place_order_rate_tracker_map_ = {};
  OrderRateTrackerMap ord_cancel_order_rate_tracker_map_ = {};
  std::unique_ptr<FixedWindowRateTracker> req_balance_rate_tracker_ = {};
  std::unique_ptr<FixedWindowRateTracker> req_position_rate_tracker_ = {};
  std::unique_ptr<FixedWindowRateTracker> req_open_order_rate_tracker_ = {};
  std::unique_ptr<FixedWindowRateTracker> req_hist_order_rate_tracker_ = {};
  std::unique_ptr<FixedWindowRateTracker> req_fill_order_rate_tracker_ = {};
  std::unique_ptr<FixedWindowRateTracker> req_deposit_rate_tracker_ = {};
  std::unique_ptr<FixedWindowRateTracker> req_withdrawal_rate_tracker_ = {};

  std::unique_ptr<RateLimitReportWriter> rate_limit_report_writer_ = {};
};

}  // namespace coin2::exchange::okex_common::api_util
